home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Dev / ACE / utils / ACEcalc / ACEcalc.b < prev    next >
Text File  |  1994-10-22  |  10KB  |  593 lines

  1. {*
  2. ** Infix Expression Workbench Calculator.
  3. **
  4. ** Uses a recursive descent expression parser.
  5. **
  6. **   Author: David J Benn
  7. **     Date: 13th-15th July 1994
  8. ** 
  9. ** Written in ACE BASIC. 
  10. *}
  11.  
  12. STRING version SIZE 40 : version = "$VER: ACEcalc 1.0 (15.07.94)"
  13.  
  14. {*** Expression Parser ***}
  15.  
  16. {*
  17. ** Operators: +,-,*,/,^,(,)
  18. ** Functions: exp,sin,cos,tan,log,sqr,int
  19. *}
  20. '..boolean constants
  21. CONST true = -1&, false = 0&
  22.  
  23. '..stack 
  24. CONST maxstack=100
  25. dim stack(maxstack)
  26. shortint stacktop
  27.  
  28. '..functions
  29. CONST maxfunc=7
  30. dim funcs$(maxfunc)
  31.  
  32. for i%=1 to maxfunc
  33.   read funcs$(i%)
  34. next
  35.  
  36. data "SIN","COS","TAN","LOG","SQR","INT","EXP"
  37.  
  38. '..symbols
  39. CONST number=1
  40. CONST plus=2
  41. CONST minus=3
  42. CONST mult=4
  43. CONST div=5
  44. CONST pow=6
  45. CONST lparen=7
  46. CONST rparen=8
  47. CONST alpha=9
  48. CONST eos=10
  49. CONST undef=11
  50.  
  51. CONST maxsym=11
  52.  
  53. '..errors
  54. longint bad
  55. CONST DIVBYZERO=1
  56. CONST SYNTAX=2
  57. CONST STKOVFL=3
  58. CONST STKUFL=4
  59.  
  60. '..variables to be shared
  61. ch$=""
  62. equ$=""
  63. obj$=""
  64. sym=undef
  65. SHORTINT n,length
  66. STRING the_expr SIZE 24
  67.  
  68. '..forward references
  69. declare SUB expr   '...factor will call this
  70.  
  71. SUB reset_parser
  72. SHARED bad, stacktop, ch$, n
  73.   bad=false
  74.   stacktop=1
  75.   ch$=" " 
  76.   n=1
  77. END SUB
  78.  
  79. SUB er(n)
  80. shared bad, the_expr
  81.   case
  82.     n=DIVBYZERO : the_expr = "DIVISION BY ZERO"
  83.     n=SYNTAX    : the_expr = "SYNTAX ERROR"
  84.     n=STKOVFL    : the_expr = "STACK OVERFLOW"
  85.     n=STKUFL    : the_expr = "STACK UNDERFLOW" '..OVFL/UFL should NOT happen!
  86.   end case
  87.   '..set error flag
  88.   bad=true
  89. END SUB
  90.  
  91. SUB nextch
  92. shared ch$,equ$,n,length
  93.  
  94.   if n<=length then
  95.     ch$=mid$(equ$,n,1)
  96.     ++n
  97.   else
  98.     ch$=""
  99.   end if 
  100. END SUB
  101.  
  102. SUB insymbol
  103. shared ch$,sym,obj$
  104. shortint periods
  105.  
  106.  obj$=""
  107.  sym=undef
  108.  
  109.  '...skip whitespace
  110.  if ch$<=" " and ch$<>"" then
  111.    repeat
  112.      nextch
  113.    until ch$>" " or ch$=""
  114.  end if
  115.  
  116.  '..end of string?
  117.  if ch$="" then sym=eos:exit sub
  118.  
  119.  '...characters
  120.  if ch$>="A" and ch$<="Z" then
  121.    while ch$>="A" and ch$<="Z"
  122.      obj$=obj$+ch$   
  123.      nextch 
  124.    wend
  125.    sym=alpha
  126.  else  
  127.    '...unsigned numeric CONSTant
  128.    if (ch$>="0" and ch$<="9") or ch$="." then
  129.      sym=number
  130.      while (ch$>="0" and ch$<="9") or ch$="."
  131.        if ch$="." then ++periods
  132.        obj$=obj$+ch$
  133.        nextch
  134.      wend
  135.      if periods > 1 then 
  136.        sym=undef
  137.        er(SYNTAX)
  138.      end if
  139.    else
  140.      '...single character
  141.      obj$=ch$
  142.      case
  143.        obj$="+" : sym=plus
  144.        obj$="-" : sym=minus
  145.        obj$="*" : sym=mult
  146.        obj$="/" : sym=div
  147.        obj$="^" : sym=pow
  148.        obj$="(" : sym=lparen
  149.        obj$=")" : sym=rparen
  150.      end case
  151.  
  152.      if sym=undef then call er(SYNTAX)
  153.      nextch
  154.    end if
  155.  end if
  156. END SUB
  157.  
  158. SUB push(x)
  159. shared stacktop,stack
  160.  
  161.   if stacktop>maxstack then 
  162.     er(STKOVFL)
  163.   else
  164.     stack(stacktop)=x
  165.     ++stacktop
  166.   end if
  167. END SUB
  168.  
  169. SUB pop
  170. shared stacktop,stack
  171.  
  172.   --stacktop
  173.   if stacktop<0 then 
  174.     er(STKUFL) 
  175.   else
  176.     pop=stack(stacktop)
  177.   end if
  178. END SUB
  179.  
  180. SUB func%
  181. shared funcs$,obj$,sym,bad
  182. longint found
  183. shortint funct
  184.  
  185.   funct=0
  186.  
  187.   found=false
  188.   i=1
  189.   while i<=maxfunc and not found
  190.     if funcs$(i) = obj$ then funct=i:found=true else ++i
  191.   wend
  192.  
  193.   if funct then 
  194.     '..function
  195.     fun$=funcs$(funct)
  196.   else
  197.     func%=0
  198.     exit sub
  199.   end if
  200.  
  201.   '...push the argument
  202.   if funct then
  203.     insymbol
  204.     if bad then func%=0:exit sub
  205.     if sym<>lparen then 
  206.       er(SYNTAX)
  207.     else
  208.       insymbol
  209.       if bad then func%=0:exit sub
  210.       expr
  211.       if sym<>rparen then call er(SYNTAX):funct=0
  212.     end if
  213.   end if
  214.  
  215.   '...which function?
  216.   case
  217.     funct=1 : push(sin(pop))
  218.     funct=2 : push(cos(pop))
  219.     funct=3 : push(tan(pop))
  220.     funct=4 : push(log(pop))
  221.     funct=5 : push(sqr(pop))
  222.     funct=6 : push(clng(pop))
  223.     funct=7 : push(exp(pop))
  224.   end case
  225.  
  226.   func%=funct
  227. END SUB
  228.  
  229. SUB factor
  230. shared sym,obj$,bad
  231.   if sym=number then 
  232.     push(val(obj$))   '...number
  233.   else
  234.     '..(expr)
  235.     if sym=lparen then
  236.       insymbol
  237.       if bad then exit sub
  238.       expr
  239.       if sym<>rparen then call er(SYNTAX)
  240.     else  
  241.       '..function?
  242.       if sym=alpha then
  243.         if func%=0 then call er(SYNTAX)
  244.       else
  245.     '..undefined
  246.         er(SYNTAX)
  247.       end if
  248.     end if
  249.   end if
  250.   insymbol
  251. END SUB
  252.  
  253. SUB expterm
  254. shared sym,bad
  255.   factor
  256.   while sym=pow
  257.     insymbol
  258.     if bad then exit sub
  259.     factor
  260.     op2=pop
  261.     op1=pop
  262.     if fix(op1)=op1 and fix(op2)=op2 then
  263.         push(clng(op1^op2))    '..suppress FFP inaccuracy
  264.     else
  265.         push(op1^op2)
  266.     end if
  267.   wend
  268. END SUB
  269.  
  270. SUB negterm
  271. shared sym,bad
  272. longint negate
  273.   negate=false
  274.   if sym=minus then negate=true:insymbol:if bad then exit sub 
  275.   if sym=plus then call insymbol:if bad then exit sub
  276.   expterm
  277.   if negate then call push(-pop)  
  278. END SUB
  279.  
  280. SUB term
  281. shared sym,bad
  282. shortint op
  283.   negterm
  284.   while sym=mult or sym=div
  285.     op=sym
  286.     insymbol
  287.     if bad then exit sub
  288.     negterm
  289.     op2=pop
  290.     op1=pop
  291.     if op=mult then
  292.       push(op1*op2)
  293.     else
  294.       if op2<>0 then 
  295.         push(op1/op2) 
  296.       else 
  297.         er(DIVBYZERO)
  298.       end if
  299.     end if
  300.   wend
  301. END SUB
  302.  
  303. SUB expr
  304. shared sym,bad
  305.   term
  306.   while sym=plus or sym=minus
  307.     op=sym
  308.     insymbol
  309.     if bad then exit sub
  310.     term
  311.     op2=pop
  312.     op1=pop
  313.     if op=plus then
  314.       push(op1+op2)
  315.     else
  316.       push(op1-op2) 
  317.     end if
  318.   wend  
  319. END SUB
  320.  
  321. SUB parse(expr$)
  322. shared sym, equ$, length, n
  323.   reset_parser
  324.   equ$ = UCASE$(expr$)
  325.   length = LEN(equ$)
  326.   insymbol
  327.   if sym=eos then exit sub
  328.   expr
  329.   if sym<>eos then call er(SYNTAX)
  330. END SUB
  331.  
  332. {* ---oOo--- *}
  333.  
  334. {*** Calculator ***}
  335.  
  336. {*
  337. ** General CONSTant declarations.
  338. *}
  339. CONST hell_freezes_over = false
  340. CONST MAXKEY = 30
  341. CONST MAXCHARS = 23
  342.  
  343. {*
  344. ** Menu CONSTant declarations.
  345. *}
  346. CONST mProject = 1
  347. CONST iAbout = 1
  348. CONST iQuit = 2
  349.  
  350.  
  351. {*
  352. ** Global variable declarations.
  353. *}
  354. STRING store SIZE 24
  355. SINGLE result
  356. DIM key$(MAXKEY)
  357.  
  358.  
  359. {*
  360. ** Subprogram declarations.
  361. *}
  362. SUB PlotKeys
  363. SHARED key$
  364. STRING k$ SIZE 4
  365. LONGINT n,xoffset
  366.  
  367.   '..top row
  368.   FOR n=1& to 5&
  369.     READ k$
  370.     key$(n) = k$
  371.     xoffset = (n-1&)*40&
  372.     GADGET n,ON,k$,(5&+xoffset,25&)-(35&+xoffset,37&),BUTTON
  373.   NEXT
  374.  
  375.   '..2nd row
  376.   FOR n=10& to 6& STEP -1
  377.     READ k$
  378.     key$(n) = k$
  379.     xoffset = (n-6&)*40& 
  380.     GADGET n,ON,k$,(5&+xoffset,40&)-(35&+xoffset,52&),BUTTON
  381.   NEXT
  382.  
  383.   '..3rd row
  384.   FOR n=11& to 15&
  385.     READ k$
  386.     key$(n) = k$
  387.     xoffset = (n-11&)*40&
  388.     GADGET n,ON,k$,(5&+xoffset,55&)-(35&+xoffset,67&),BUTTON
  389.   NEXT
  390.  
  391.   '..4th row
  392.   FOR n=20& to 16& STEP -1
  393.     READ k$
  394.     key$(n) = k$
  395.     xoffset = (n-16&)*40&
  396.     GADGET n,ON,k$,(5&+xoffset,70&)-(35&+xoffset,82&),BUTTON
  397.   NEXT
  398.  
  399.   '..5th row
  400.   FOR n=21& to 25&
  401.     READ k$
  402.     key$(n) = k$
  403.     xoffset = (n-21&)*40&
  404.     GADGET n,ON,k$,(5&+xoffset,85&)-(35&+xoffset,97&),BUTTON
  405.   NEXT
  406.  
  407.   '..6th row
  408.   FOR n=30& to 26& STEP -1
  409.     READ k$
  410.     key$(n) = k$
  411.     xoffset = (n-26&)*40&
  412.     GADGET n,ON,k$,(5&+xoffset,100&)-(35&+xoffset,112&),BUTTON
  413.   NEXT
  414.  
  415.   '..key data
  416.   DATA "7","8","9","(",")"        '..top row
  417.   DATA "-","+","6","5","4"        '..2nd row
  418.   DATA "1","2","3","*","/"        '..3rd row
  419.   DATA "«-","^","=",".","0"        '..4th row
  420.   DATA "CLR","STO","RCL","INT","EXP"    '..5th row
  421.   DATA "SQR","LOG","TAN","COS","SIN"    '..6th row
  422. END SUB
  423.  
  424. SUB SetUpMenus
  425.   '..Project menu
  426.   MENU mProject,0,1,"Project"
  427.   MENU mProject,iAbout,1,     "About..." 
  428.   MENU mProject,iQuit,1,      "Quit","Q"
  429. END SUB
  430.  
  431. SUB update_display
  432. SHARED the_expr
  433. {*
  434. ** Update expression display.
  435. *}
  436.   LINE (7,5)-(192,17),0,bf
  437.   LOCATE 2,2
  438.   PRINT the_expr;
  439. END SUB
  440.  
  441. SUB operation(key_num)
  442. SHARED key$, the_expr, store
  443. SHARED result, bad, length
  444. {*
  445. ** Act upon selected key.
  446. *}
  447.  
  448.   IF bad THEN
  449.     '..Recover from recent error by
  450.     '..resetting parser and calculator.
  451.     reset_parser
  452.     the_expr = ""
  453.     update_display
  454.   END IF
  455.  
  456.   IF key$(key_num) = "=" THEN
  457.     '..Compute result
  458.     IF the_expr <> "" THEN
  459.       parse(the_expr)
  460.       IF NOT bad THEN 
  461.         result = pop
  462.         the_expr = STR$(result)
  463.       END IF
  464.       IF LEFT$(the_expr,1) = " " THEN the_expr = MID$(the_expr,2)
  465.       update_display
  466.     END IF
  467.     EXIT SUB
  468.   END IF
  469.  
  470.   IF key$(key_num) = "STO" THEN
  471.     '..Store current expression
  472.     store = the_expr
  473.     EXIT SUB
  474.   END IF
  475.  
  476.   IF key$(key_num) = "RCL" THEN
  477.     '..Recall stored expression
  478.     IF LEN(the_expr)+LEN(store) <= MAXCHARS THEN the_expr = the_expr+store
  479.     update_display
  480.     EXIT SUB
  481.   END IF
  482.  
  483.   IF key$(key_num) = "CLR" THEN 
  484.     '..Clear expression
  485.     the_expr = ""
  486.     update_display
  487.     EXIT SUB
  488.   END IF
  489.  
  490.   IF key$(key_num) = "«-" THEN
  491.     '..Remove right-most character
  492.     the_expr = LEFT$(the_expr,LEN(the_expr)-1)
  493.     update_display
  494.     EXIT SUB
  495.   END IF
  496.   
  497.   '..For all other keys -> Update expression
  498.   IF LEN(the_expr)+LEN(key$(key_num)) <= MAXCHARS THEN 
  499.     the_expr = the_expr+key$(key_num)
  500.     update_display
  501.   END IF
  502. END SUB
  503.  
  504. SUB check_for_keypress(k$)
  505. SHARED key$
  506. SHORTINT n
  507. {*
  508. ** Has a physical key been pressed?
  509. *}
  510.   IF k$<>"" THEN
  511.     '..Was the return/enter key pressed?
  512.     '..(treat as "equal" key)
  513.     IF k$=CHR$(13) THEN k$ = "="
  514.  
  515.     '..Was the destructive backspace
  516.     '..or DEL key pressed?
  517.     IF k$=CHR$(8) OR k$=CHR$(127) THEN k$ = "«-"
  518.  
  519.     '..Is it a calculator key?
  520.     FOR n=1 to MAXKEY
  521.       IF k$ = key$(n) THEN EXIT FOR
  522.     NEXT
  523.  
  524.     '..Act on it!
  525.     IF n>=1 AND n<=MAXKEY THEN CALL operation(n)
  526.   END IF
  527. END SUB
  528.  
  529. SUB service_menu(x,y)
  530.   IF x = mProject THEN
  531.     IF y = iAbout THEN 
  532.           res = MsgBox("Copyright © David Benn, 1994","Continue")
  533.     EXIT SUB     
  534.     END IF
  535.  
  536.     IF y = iQuit THEN GOSUB quit
  537.   END IF
  538. END SUB
  539.  
  540.  
  541. {*
  542. ** Main program.
  543. *}
  544. WINDOW 1,"ACEcalc v1.0",(220,75)-(428,205),30
  545.  
  546. BEVELBOX (5,4)-(194,18),2
  547.  
  548. FONT "topaz",8
  549. STYLE 2    '..bold
  550.  
  551. PlotKeys 
  552. SetUpMenus
  553.  
  554. ON WINDOW GOSUB quit
  555. ON GADGET GOSUB handle_gadget
  556. ON MENU GOSUB handle_menu
  557.  
  558. WINDOW ON
  559. GADGET ON
  560. MENU ON
  561.  
  562. REPEAT
  563.   SLEEP
  564.   check_for_keypress(INKEY$)
  565. UNTIL hell_freezes_over
  566.  
  567. {* ---oOo--- *}
  568.  
  569.  
  570. {* 
  571. ** Event handlers.
  572. *}
  573. handle_gadget:
  574.   operation(GADGET(1))
  575. RETURN
  576.  
  577. handle_menu:
  578.   service_menu(MENU(0),MENU(1))
  579. RETURN
  580.  
  581. quit:
  582.   {*IF NOT MsgBox("Really want to quit?","Yes","No!") THEN 
  583.     RETURN
  584.   ELSE*}
  585.     '..Clean up and exit.
  586.     MENU CLEAR 
  587.     FOR i=1 to MAXKEY
  588.       GADGET CLOSE i
  589.     NEXT
  590.     WINDOW CLOSE 1
  591.   'END IF
  592. END
  593.